#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include "cRTSPClient.h"
#include"memwatch.h"
#include "sample_comm.h"
#define FU_A_F 28

static ot_payload_type g_cur_type = OT_PT_H265;

static vdec_display_cfg g_vdec_display_cfg = {
    .pic_size = PIC_1080P, //1920x1080 PIC_D1_PAL
    .intf_sync = OT_VO_OUT_1080P60,
    .intf_type = OT_VO_INTF_BT1120,
};

static ot_size g_disp_size;
static td_s32 g_sample_exit = 0;
#define PIC_SIZE   PIC_3840X2160
#define SAMPLE_STREAM_PATH "/"
#define UHD_STREAN_WIDTH 1920
#define UHD_STREAM_HEIGHT 1080
#define FHD_STREAN_WIDTH 1920
#define FHD_STREAN_HEIGHT 1080
#define REF_NUM 2
#define DISPLAY_NUM 1

static td_u32 sample_vdec_get_chn_width(td_void)
{
    switch (g_cur_type) {
        case OT_PT_H264:
        case OT_PT_H265:
            return UHD_STREAN_WIDTH;
        case OT_PT_JPEG:
        case OT_PT_MJPEG:
            return UHD_STREAN_WIDTH;
        default:
            sample_print("invalid type %d!\n", g_cur_type);
            return UHD_STREAN_WIDTH;
    }
}

static td_u32 sample_vdec_get_chn_height(td_void)
{
    switch (g_cur_type) {
        case OT_PT_H264:
        case OT_PT_H265:
            return UHD_STREAM_HEIGHT;
        case OT_PT_JPEG:
        case OT_PT_MJPEG:
            return UHD_STREAM_HEIGHT;
        default:
            sample_print("invalid type %d!\n", g_cur_type);
            return UHD_STREAM_HEIGHT;
    }
}

static td_s32 sample_init_module_vb(sample_vdec_attr *sample_vdec, td_u32 vdec_chn_num, ot_payload_type type,
    td_u32 len)
{
    td_u32 i;
    td_s32 ret;
    for (i = 0; (i < vdec_chn_num) && (i < len); i++) {
        sample_vdec[i].type                        = type;
        sample_vdec[i].width                       = sample_vdec_get_chn_width();
        sample_vdec[i].height                      = sample_vdec_get_chn_height();
        sample_vdec[i].mode                        = OT_VDEC_SEND_MODE_FRAME;// OT_VDEC_SEND_MODE_COMPAT OT_VDEC_SEND_MODE_FRAME OT_VDEC_SEND_MODE_STREAM
        sample_vdec[i].sample_vdec_video.dec_mode  = OT_VIDEO_DEC_MODE_IP;
        sample_vdec[i].sample_vdec_video.bit_width = OT_DATA_BIT_WIDTH_8;
        if (type == OT_PT_JPEG) {
            sample_vdec[i].sample_vdec_video.ref_frame_num = 0;
        } else {
            sample_vdec[i].sample_vdec_video.ref_frame_num = REF_NUM;
        }
        sample_vdec[i].display_frame_num               = DISPLAY_NUM;
        sample_vdec[i].frame_buf_cnt = (type == OT_PT_JPEG) ? (sample_vdec[i].display_frame_num + 1) :
            (sample_vdec[i].sample_vdec_video.ref_frame_num + sample_vdec[i].display_frame_num + 1);
        if (type == OT_PT_JPEG) {
            sample_vdec[i].sample_vdec_picture.pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
            sample_vdec[i].sample_vdec_picture.alpha      = 255; /* 255:pic alpha value */
        }
    }
    ret = sample_comm_vdec_init_vb_pool(vdec_chn_num, &sample_vdec[0], len);
    if (ret != TD_SUCCESS) {
        sample_print("init mod common vb fail for %#x!\n", ret);
        return ret;
    }
#ifdef SAMPLE_MEM_SHARE_ENABLE
    sample_init_mem_share();
#endif
    return ret;
}

static td_s32 sample_init_sys_and_vb(sample_vdec_attr *sample_vdec, td_u32 vdec_chn_num, ot_payload_type type,
    td_u32 len)
{
    ot_vb_cfg vb_cfg;
    ot_pic_buf_attr buf_attr = {0};
    td_s32 ret;

    ret = sample_comm_sys_get_pic_size(g_vdec_display_cfg.pic_size, &g_disp_size);
    if (ret != TD_SUCCESS) {
        sample_print("sys get pic size fail for %#x!\n", ret);
        return ret;
    }
    printf("g_disp_size.height = %d, g_disp_size.width = %d\n",g_disp_size.height, g_disp_size.width);
    buf_attr.align = 0;
    buf_attr.bit_width = OT_DATA_BIT_WIDTH_8;
    buf_attr.compress_mode = OT_COMPRESS_MODE_SEG;
    buf_attr.height = g_disp_size.height;
    buf_attr.width = g_disp_size.width;
    buf_attr.pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    //buf_attr.pixel_format = OT_PIXEL_FORMAT_RGB_888;

    (td_void)memset_s(&vb_cfg, sizeof(ot_vb_cfg), 0, sizeof(ot_vb_cfg));
    vb_cfg.max_pool_cnt             = 1;
    vb_cfg.common_pool[0].blk_cnt  = 10 * vdec_chn_num; /* 10:common vb cnt */
    vb_cfg.common_pool[0].blk_size = ot_common_get_pic_buf_size(&buf_attr);
    ret = sample_comm_sys_vb_init(&vb_cfg);
    if (ret != TD_SUCCESS) {
        sample_print("init sys fail for %#x!\n", ret);
        sample_comm_sys_exit();
        return ret;
    }
    ret = sample_init_module_vb(&sample_vdec[0], vdec_chn_num, type, len);
    if (ret != TD_SUCCESS) {
        sample_print("init mod vb fail for %#x!\n", ret);
        sample_comm_vdec_exit_vb_pool();
        sample_comm_sys_exit();
        return ret;
    }
    return ret;
}

static td_s32 sample_start_vdec(sample_vdec_attr *sample_vdec, td_u32 vdec_chn_num, td_u32 len)
{
    td_s32 ret;

    ret = sample_comm_vdec_start(vdec_chn_num, &sample_vdec[0], len);
    if (ret != TD_SUCCESS) {
        sample_print("start VDEC fail for %#x!\n", ret);
        sample_comm_vdec_stop(vdec_chn_num);
    }

    return ret;
}

static td_void sample_stop_vpss(ot_vpss_grp vpss_grp, td_bool *vpss_chn_enable, td_u32 chn_array_size)
{
    td_s32 i;
    for (i = vpss_grp; i >= 0; i--) {
        vpss_grp = i;
        sample_common_vpss_stop(vpss_grp, &vpss_chn_enable[0], chn_array_size);
    }
}

static td_void sample_config_vpss_grp_attr(ot_vpss_grp_attr *vpss_grp_attr)
{
    vpss_grp_attr->max_width = sample_vdec_get_chn_width();
    vpss_grp_attr->max_height = sample_vdec_get_chn_height();
    vpss_grp_attr->frame_rate.src_frame_rate = -1;
    vpss_grp_attr->frame_rate.dst_frame_rate = -1;
    vpss_grp_attr->pixel_format  = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    vpss_grp_attr->ie_en   = TD_FALSE;
    vpss_grp_attr->dci_en   = TD_FALSE;
    vpss_grp_attr->dei_mode = OT_VPSS_DEI_MODE_OFF;
    vpss_grp_attr->buf_share_en   = TD_FALSE;
}

static td_s32 sample_config_vpss_ldy_attr(td_u32 vpss_grp_num)
{
    ot_vpss_grp grp;
    ot_low_delay_info vpss_ldy_info;

    if (sample_comm_vdec_get_lowdelay_en() == TD_FALSE) {
        return TD_SUCCESS;
    }

    for (grp = 0; grp < (td_s32)vpss_grp_num; grp++) {
        check_chn_return(ss_mpi_vpss_get_chn_low_delay(grp, 0, &vpss_ldy_info), grp, "ss_mpi_vpss_get_chn_low_delay");
        vpss_ldy_info.enable = TD_TRUE;
        vpss_ldy_info.line_cnt = 16; /* 16:VDEC_LOW_DELAY_MIN_LINE */
        check_chn_return(ss_mpi_vpss_set_chn_low_delay(grp, 0, &vpss_ldy_info), grp, "ss_mpi_vpss_set_chn_low_delay");
    }

    return TD_SUCCESS;
}

static td_s32 sample_vdec_bind_vpss(td_u32 vpss_grp_num)
{
    td_u32 i;
    td_s32 ret = TD_SUCCESS;
    for (i = 0; i < vpss_grp_num; i++) {
        ret = sample_comm_vdec_bind_vpss(i, i);
        if (ret != TD_SUCCESS) {
            sample_print("vdec bind vpss fail for %#x!\n", ret);
            return ret;
        }
    }
    return ret;
}

static td_void sample_vdec_unbind_vpss(td_u32 vpss_grp_num)
{
    td_u32 i;
    td_s32 ret;

    for (i = 0; i < vpss_grp_num; i++) {
        ret = sample_comm_vdec_un_bind_vpss(i, i);
        if (ret != TD_SUCCESS) {
            sample_print("vdec unbind vpss fail for %#x!\n", ret);
        }
    }
    return;
}

static td_s32 sample_start_vpss(ot_vpss_grp *vpss_grp, td_u32 vpss_grp_num, td_bool *vpss_chn_enable, td_u32 arr_len)
{
    td_u32 i;
    td_s32 ret;
    sample_vpss_chn_attr vpss_chn_attr;
    ot_vpss_grp_attr vpss_grp_attr;

    sample_config_vpss_grp_attr(&vpss_grp_attr);
    (td_void)memset_s(vpss_chn_enable, arr_len * sizeof(td_bool), 0, arr_len * sizeof(td_bool));
    sample_comm_vpss_get_default_chn_attr(&vpss_chn_attr.chn_attr[0]);
    vpss_chn_enable[0]      = TD_TRUE;
    vpss_chn_attr.chn_enable[0] = TD_TRUE;
    vpss_chn_attr.chn_array_size = OT_VPSS_MAX_CHN_NUM;
    vpss_chn_attr.chn_attr[0].width  = g_disp_size.width;
    vpss_chn_attr.chn_attr[0].height = g_disp_size.height;
    vpss_chn_attr.chn_attr[0].chn_mode = OT_VPSS_CHN_MODE_AUTO;

    for (i = 0; i < vpss_grp_num; i++) {
        *vpss_grp = i;
        ret = sample_common_vpss_start(*vpss_grp, &vpss_grp_attr, &vpss_chn_attr);
        if (ret != TD_SUCCESS) {
            sample_print("start VPSS fail for %#x!\n", ret);
            sample_stop_vpss(*vpss_grp, &vpss_chn_enable[0], OT_VPSS_MAX_CHN_NUM);
            return ret;
        }
    }

    ret = sample_config_vpss_ldy_attr(vpss_grp_num);
    if (ret != TD_SUCCESS) {
        sample_stop_vpss(*vpss_grp, &vpss_chn_enable[0], OT_VPSS_MAX_CHN_NUM);
        return ret;
    }
#if 0
    ret = sample_vdec_bind_vpss(vpss_grp_num);
    if (ret != TD_SUCCESS) {
        sample_vdec_unbind_vpss(vpss_grp_num);
        sample_stop_vpss(*vpss_grp, &vpss_chn_enable[0], OT_VPSS_MAX_CHN_NUM);
    }
#endif
    return ret;
}

static td_s32 sample_start_vo(sample_vo_cfg *vo_config, td_u32 vpss_grp_num)
{
    td_s32 ret;
    vo_config->vo_dev            = SAMPLE_VO_DEV_UHD;
    vo_config->vo_layer          = SAMPLE_VO_LAYER_VHD0;
    vo_config->vo_intf_type      = g_vdec_display_cfg.intf_type;
    vo_config->intf_sync         = g_vdec_display_cfg.intf_sync;
    vo_config->pic_size          = g_vdec_display_cfg.pic_size;
    vo_config->bg_color          = COLOR_RGB_BLUE;
    vo_config->dis_buf_len       = 3; /* 3:buf length */
    vo_config->dst_dynamic_range = OT_DYNAMIC_RANGE_SDR8;
    vo_config->vo_mode           = VO_MODE_1MUX;
    vo_config->pix_format        = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    //vo_config->pix_format        = OT_PIXEL_FORMAT_RGB_888;
    vo_config->disp_rect.x       = 0;
    vo_config->disp_rect.y       = 0;
    vo_config->disp_rect.width   = g_disp_size.width;
    vo_config->disp_rect.height  = g_disp_size.height;
    vo_config->image_size.width  = g_disp_size.width;
    vo_config->image_size.height = g_disp_size.height;
    vo_config->vo_part_mode      = OT_VO_PARTITION_MODE_SINGLE;
    vo_config->compress_mode     = OT_COMPRESS_MODE_NONE;

    ret = sample_comm_vo_start_vo(vo_config);
    if (ret != TD_SUCCESS) {
        sample_print("start VO fail for %#x!\n", ret);
        sample_comm_vo_stop_vo(vo_config);
        return ret;
    }

#ifdef SAMPLE_MEM_SHARE_ENABLE
    sample_init_vo_mem_share(vo_config->vo_layer);
#endif
	#if 0
    ret = sample_vpss_bind_vo(vo_config, vpss_grp_num);
    if (ret != TD_SUCCESS) {
        sample_vpss_unbind_vo(vpss_grp_num, vo_config);
        sample_comm_vo_stop_vo(vo_config);
    }
	#endif
    return ret;
}

static td_s32 sample_h264_vdec_vpss_vo(td_void)
{
    td_s32 ret;
    td_u32 vdec_chn_num, vpss_grp_num;
    ot_vpss_grp vpss_grp;
    sample_vdec_attr sample_vdec[OT_VDEC_MAX_CHN_NUM];
    sample_vo_cfg vo_config;
    td_bool vpss_chn_enable[OT_VPSS_MAX_CHN_NUM];

    vdec_chn_num = 1;
    vpss_grp_num = vdec_chn_num;
    g_cur_type = OT_PT_H264;
    /************************************************
    step1:  init SYS, init common VB(for VPSS and VO), init module VB(for VDEC)
    *************************************************/
    ret = sample_init_sys_and_vb(&sample_vdec[0], vdec_chn_num, g_cur_type, OT_VDEC_MAX_CHN_NUM);
    if (ret != TD_SUCCESS) {
        return ret;
    }
    /************************************************
    step2:  init VDEC
    *************************************************/
    ret = sample_start_vdec(&sample_vdec[0], vdec_chn_num, OT_VDEC_MAX_CHN_NUM);
    if (ret != TD_SUCCESS) {
        goto stop_sys;
    }
    /************************************************
    step3:  start VPSS
    *************************************************/
    ret = sample_start_vpss(&vpss_grp, vpss_grp_num, &vpss_chn_enable[0], OT_VPSS_MAX_CHN_NUM);
    if (ret != TD_SUCCESS) {
        goto stop_vdec;
    }
    /************************************************
    step4:  start VO
    *************************************************/
    ret = sample_start_vo(&vo_config, vpss_grp_num);
    if (ret != TD_SUCCESS) {
        goto stop_vpss;
    }

	return ret;
    
stop_vpss:
    sample_vdec_unbind_vpss(vpss_grp_num);
    sample_stop_vpss(vpss_grp, &vpss_chn_enable[0], OT_VPSS_MAX_CHN_NUM);
	//sample_vpss_unbind_vo(vpss_grp_num, &vo_config);
    sample_comm_vo_stop_vo(&vo_config);
stop_vdec:
    sample_comm_vdec_stop(vdec_chn_num);
    sample_comm_vdec_exit_vb_pool();
stop_sys:
    sample_comm_sys_exit();

    return ret;
}

#define VDEC_ON
int main(int argc, const char *argv[])
{
#ifdef EN_MEMWATCH
	mwInit();
#endif
	cRTSPClient *client = new cRTSPClient();
	unsigned char *pdata = NULL;
	int dataLen =0;
	int flag = 0;
    int ret = 0;
    struct timeval tv1, tv2;
	//char nal[4]={0x00,0x00,0x00,0x01};
    char nal[3]={0x00,0x00,0x01};
	ot_vdec_stream stream;
	td_bool end_of_stream;
	ot_video_frame_info frame_info;
	td_u64 pts = 0;
	FILE *fp_out = fopen("./live_out","w+");
	if(fp_out == NULL)
	{
		printf("erro to open file\n");
		return -1;
	}
	#ifdef VDEC_ON
	sample_h264_vdec_vpss_vo();
	end_of_stream = TD_FALSE;
	#endif
	if(argc != 2)
	{	//char defUrl[]="rtsp://127.0.0.1:8989/stream";
		char defUrl[]= "rtsp://192.168.110.173:554/mainstream";
		printf("usage:url\n");
		printf("use defalut url:%s\n",defUrl);
		
		client->api_init(defUrl);
	}
	else
	{
		client->api_init(argv[1]);
	}
	
	int count = 0;
	//while(1);
	while(1)
	{
		//usleep(200000);
		//printf("api_requestFrameBuffer\n");
		int index =client->api_requestFrameBuffer(&pdata, &dataLen);
		#if 0
		if(index >= 0)
		{
			printf("write dataLend =%d\n", dataLen);
			fwrite(nal,1,sizeof(nal),fp_out);
			fwrite(pdata,1,dataLen,fp_out);
			client->api_releaseBuffer(index);
		}
		#endif
		#if 1
		if(index >= 0) {
			stream.pts = pts;
			stream.addr = pdata;
			stream.len = dataLen;
			stream.end_of_frame =  TD_FALSE;
			stream.need_display = TD_TRUE;
			pts++;
			//printf("dataLen = %d\n", dataLen);
			if(dataLen == 0) {
				//printf("head vdec\n");
				stream.end_of_frame =  TD_FALSE;
				stream.end_of_stream = end_of_stream;
				count = ss_mpi_vdec_send_stream(0, &stream, 10);
				if (count == 0) {
					printf("send stream to vdec success\n");
				} else {
					printf("send stream to vdec failed 0x%x\n", count);
				}
			} else if(dataLen > 25) {
				//printf("data vdec\n");
				flag++;
				end_of_stream = TD_FALSE;
				stream.end_of_stream = end_of_stream;
				stream.end_of_frame = TD_TRUE;
                gettimeofday(&tv1, NULL);
				count = ss_mpi_vdec_send_stream(0, &stream, -1);
                gettimeofday(&tv2, NULL);
				if (count == 0) {
					printf("send stream to vdec success\n");
				} else {
					printf("send stream to vdec failed 0x%x\n", count);
				}
                printf("send stream to vdec use %ld us\n", ((tv2.tv_usec -tv1.tv_usec) / 1) + 1000000*(tv2.tv_sec - tv1.tv_sec));
				end_of_stream = TD_FALSE;
				if(flag > 0) {
					//printf("data vdec 1 flag > 2\n");
                    gettimeofday(&tv1, NULL);
					count = ss_mpi_vdec_get_frame(0, &frame_info, NULL, 10);
                    gettimeofday(&tv2, NULL);
					if(count == 0) {	
						printf("ss_mpi_vdec_get_frame success\n");
					} else {
						printf("ss_mpi_vdec_get_frame failed 0x%x\n", count);
					}
                    printf("get stream from vdec use %ld us\n", ((tv2.tv_usec -tv1.tv_usec) / 1) + 1000000*(tv2.tv_sec - tv1.tv_sec));
					//printf("data vdec 2\n");
                    ret = ss_mpi_vdec_release_frame(0, &frame_info);
                    if (ret == 0) {
                        printf("release vdec frame success\n");
                    } else {
                        printf("release vdec frame failed 0x%x\n", ret);
                    }
                    if(count == 0) {
                        gettimeofday(&tv1, NULL);
                        count = ss_mpi_vo_send_frame(0, 0, &frame_info, -1);
                        gettimeofday(&tv2, NULL);
					    //printf("data vdec 3\n");
					    if(count == 0) {
						    printf("send vo fraeme success\n");
					    } else {
						    printf("send vo frame failed 0x%x\n", count);
					    }
                        printf("send stream to vo use %ld us\n", ((tv2.tv_usec -tv1.tv_usec) / 1) + 1000000*(tv2.tv_sec - tv1.tv_sec));
                    }
				}
			}
			client->api_releaseBuffer(index);
		}
		#endif
		#if 0
		if(pdata != NULL) {
			stream.pts = pts;
			stream.addr = pdata+2;
			stream.len = dataLen-2;
			stream.end_of_frame =  TD_TRUE;
			stream.need_display = TD_TRUE;
			printf("hh %d\n", pdata[0] & 0x1f);
			if(((pdata[0] & 0x1f) < 24) && ((pdata[0] & 0x1f) > 0)) {
				//printf("nal unit\n");
				stream.end_of_stream = end_of_stream;
				ss_mpi_vdec_send_stream(0, &stream, -1);
				//printf("nal unit fini\n");
			} else if((pdata[0] & 0x1f) == FU_A_F) {
				if(pdata[1] & 0x80) { //分片第一包
					//printf("fp s\n");
					stream.end_of_stream = end_of_stream;
					ss_mpi_vdec_send_stream(0, &stream, -1);
					//printf("fp e\n");
				} else if(pdata[1] & 0x40) { //分片最后一包
					printf("fp ls\n");
					end_of_stream = TD_TRUE;
					stream.end_of_stream = end_of_stream;
					ss_mpi_vdec_send_stream(0, &stream, -1);
					end_of_stream = TD_FALSE;
					count = ss_mpi_vdec_get_frame(0, &frame_info, NULL, -1);
					if(count == 0) {
						printf("ss_mpi_vdec_get_frame fialed 0x%x\n", count);
					} else {
						printf("ss_mpi_vdec_get_frame success\n");
					}
					count = ss_mpi_vo_send_frame(0, 0, &frame_info, -1);
					if(count == 0) {
						printf("send vo frame fialed 0x%x\n", count);
					} else {
						printf("send vo fraeme success\n");
					}
					printf("fp le\n");
				} else { //分片中的包
					printf("fp in\n");
					stream.end_of_stream = end_of_stream;
					ss_mpi_vdec_send_stream(1, &stream, -1);
					printf("fp fi\n");
				}
			}
		}
		#endif
	}
	fclose(fp_out);
	client->api_deinit();
	delete client;

	printf("wang over-----\n\n");
	
#ifdef EN_MEMWATCH
	mwTerm();
#endif
	return 0;
}
